package battlecruiser.ship.placement.shipyard;

import battlecruiser.board.ObservableBoard;
import battlecruiser.board.cell.Cell;
import battlecruiser.board.cell.CellStatus;
import battlecruiser.board.cell.coordinate.Coordinate;
import battlecruiser.ship.placement.fleet.Fleet;
import battlecruiser.ship.placement.fleet.FleetFactory;
import battlecruiser.ship.placement.ship.Ship;

import java.util.List;

import static battlecruiser.board.ObservableBoard.isWithinTheBoard;

/**
 * @author tymoshya
 * @since 15.06.12
 */
public abstract class Shipyard {

    protected Fleet fleet;
    protected ObservableBoard observableBoard;

    public Shipyard(ObservableBoard observableBoard) {
        this.observableBoard = observableBoard;
        fleet = FleetFactory.createEmptyFleet();
        observableBoard.setFleet(fleet);
    }


    public abstract void deployFleet();

    public void deployShip(Ship ship) throws ShipDeploymentException {
        checkIsWithinTheBoard(ship);
        checkDoesNotTouchOtherShip(ship);
        if (fleet.addShip(ship)) {
            observableBoard.drawShip(ship);
        } else {
            throw new ShipDeploymentException("Can't place any more ships of this type!");
        }
    }

    public ObservableBoard getObservableBoard() {
        return observableBoard;
    }

    public Fleet getFleet() {
        return fleet;
    }

    public void setFleet(Fleet fleet) {
        this.fleet = fleet;
    }

    private void checkIsWithinTheBoard(Ship ship) throws ShipDeploymentException {
        Coordinate firstDeck = ship.getFirstDeckCoordinate();
        Coordinate lastDeck = ship.getLastDeckCoordinate();
        if (!isWithinTheBoard(firstDeck)) {
            throw new ShipDeploymentException(String.format("First deck cell is out of the board! -> Cell[%s][%s]"
                    , firstDeck.getJ(), firstDeck.getI()));
        }
        if (!isWithinTheBoard(lastDeck)) {
            throw new ShipDeploymentException(String.format("Last deck cell is out of the board! -> Cell[%s][%s]"
                    , lastDeck.getJ(), lastDeck.getI()));
        }
    }

    private void checkDoesNotTouchOtherShip(Ship ship) throws ShipDeploymentException {
        // Can only place ship in "Free" cell
        // assuming isWithinTheBoard checking was already done
        List<Coordinate> shipCoordinates = ship.getCoordinates();
        Cell[][] gameBoardCells = observableBoard.getCells();
        for (Coordinate coordinate : shipCoordinates) {
            if (gameBoardCells[coordinate.getI()][coordinate.getJ()].getStatus() != CellStatus.FREE) {
                throw new ShipDeploymentException(String.format("Ships are touching at Coordinate %s. Check the board: \n%s"
                        , coordinate, observableBoard));
            }
        }
    }
}
